<!DOCTYPE html>
<html lang="zh-CN" dir="ltr">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>14.3 Flutter启动流程和渲染管线 | 落光的Pro博客</title>
    <meta name="description" content="Vite & Vue powered static site generator.">
    <link rel="preload stylesheet" href="./assets/style.4b0df91d.css" as="style">
    
    <script type="module" src="./assets/app.0acbd3fd.js"></script>
    <link rel="preload" href="./assets/inter-roman-latin.2ed14f66.woff2" as="font" type="font/woff2" crossorigin="">
    <link rel="modulepreload" href="./assets/chunks/framework.eaf25f5b.js">
    <link rel="modulepreload" href="./assets/chunks/theme.ce44a0e6.js">
    <link rel="modulepreload" href="./assets/chunks/14-4.c0e2d311.js">
    <link rel="modulepreload" href="./assets/largeFrontEnd_flutter_chapter14_flutter_app_startup.md.9b5942a1.lean.js">
    <script id="check-dark-light">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
  </head>
  <body>
    <div id="app"><div class="Layout" data-v-1919c326><!--[--><!--]--><!--[--><span tabindex="-1" data-v-0f60ec36></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-0f60ec36> Skip to content </a><!--]--><!----><header class="VPNav" data-v-1919c326 data-v-7e5bc4a5><div class="VPNavBar has-sidebar" data-v-7e5bc4a5 data-v-a0fd61f4><div class="container" data-v-a0fd61f4><div class="title" data-v-a0fd61f4><div class="VPNavBarTitle has-sidebar" data-v-a0fd61f4 data-v-86d1bed8><a class="title" href="./" data-v-86d1bed8><!--[--><!--]--><!--[--><img class="VPImage logo" src="./icon-radius.png" alt data-v-8426fc1a><!--]--><!--[-->落光的Pro博客<!--]--><!--[--><!--]--></a></div></div><div class="content" data-v-a0fd61f4><div class="curtain" data-v-a0fd61f4></div><div class="content-body" data-v-a0fd61f4><!--[--><!--]--><div class="VPNavBarSearch search" style="--vp-meta-key:&#39;Meta&#39;;" data-v-a0fd61f4><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><svg class="DocSearch-Search-Icon" width="20" height="20" viewBox="0 0 20 20" aria-label="search icon"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-a0fd61f4 data-v-7f418b0f><span id="main-nav-aria-label" class="visually-hidden" data-v-7f418b0f>Main Navigation</span><!--[--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-7f418b0f data-v-9c007e85><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-9c007e85><span class="text" data-v-9c007e85><!----><span data-v-9c007e85>大前端</span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-9c007e85><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-9c007e85><div class="VPMenu" data-v-9c007e85 data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link" href="./largeFrontEnd/webFrontEnd/md/01.html" data-v-43f1e123><!--[-->Web前端<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link" href="./largeFrontEnd/uniapp/" data-v-43f1e123><!--[-->Uni-app<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link" href="./largeFrontEnd/weChatMiniProgram/" data-v-43f1e123><!--[-->微信小程序<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link" href="./largeFrontEnd/electron/" data-v-43f1e123><!--[-->Electron<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link" href="./largeFrontEnd/flutter/" data-v-43f1e123><!--[-->Flutter<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link" href="./largeFrontEnd/interview/01.html" data-v-43f1e123><!--[-->前端面试题<!--]--></a></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="./backEnd/" tabindex="0" data-v-7f418b0f data-v-42ef59de><!--[--><span data-v-42ef59de>后端</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="./linux/" tabindex="0" data-v-7f418b0f data-v-42ef59de><!--[--><span data-v-42ef59de>Linux</span><!--]--></a><!--]--><!--[--><a class="VPLink link VPNavBarMenuLink" href="./myTeam/" tabindex="0" data-v-7f418b0f data-v-42ef59de><!--[--><span data-v-42ef59de>我的团队</span><!--]--></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-a0fd61f4 data-v-f6a63727><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="toggle dark mode" aria-checked="false" data-v-f6a63727 data-v-ce54a7d1 data-v-b1685198><span class="check" data-v-b1685198><span class="icon" data-v-b1685198><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-ce54a7d1><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-ce54a7d1><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-a0fd61f4 data-v-0394ad82 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://gitee.com/luoguangguang" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><a class="VPSocialLink no-icon" href="..." aria-label="cool link" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Dribbble</title><path d="M12...6.38z"/></svg></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-a0fd61f4 data-v-40855f84 data-v-9c007e85><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-9c007e85><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="icon" data-v-9c007e85><circle cx="12" cy="12" r="2"></circle><circle cx="19" cy="12" r="2"></circle><circle cx="5" cy="12" r="2"></circle></svg></button><div class="menu" data-v-9c007e85><div class="VPMenu" data-v-9c007e85 data-v-e7ea1737><!----><!--[--><!--[--><!----><div class="group" data-v-40855f84><div class="item appearance" data-v-40855f84><p class="label" data-v-40855f84>Appearance</p><div class="appearance-action" data-v-40855f84><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="toggle dark mode" aria-checked="false" data-v-40855f84 data-v-ce54a7d1 data-v-b1685198><span class="check" data-v-b1685198><span class="icon" data-v-b1685198><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-ce54a7d1><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-ce54a7d1><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div></div></div><div class="group" data-v-40855f84><div class="item social-links" data-v-40855f84><div class="VPSocialLinks social-links-list" data-v-40855f84 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://gitee.com/luoguangguang" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><a class="VPSocialLink no-icon" href="..." aria-label="cool link" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Dribbble</title><path d="M12...6.38z"/></svg></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-a0fd61f4 data-v-e5dd9c1c><span class="container" data-v-e5dd9c1c><span class="top" data-v-e5dd9c1c></span><span class="middle" data-v-e5dd9c1c></span><span class="bottom" data-v-e5dd9c1c></span></span></button></div></div></div></div><!----></header><div class="VPLocalNav reached-top" data-v-1919c326 data-v-79c8c1df><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-79c8c1df><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="menu-icon" data-v-79c8c1df><path d="M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z"></path><path d="M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z"></path><path d="M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z"></path><path d="M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z"></path></svg><span class="menu-text" data-v-79c8c1df>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-79c8c1df data-v-1c15a60a><button data-v-1c15a60a>Return to top</button><!----></div></div><aside class="VPSidebar" data-v-1919c326 data-v-b00e2fdd><div class="curtain" data-v-b00e2fdd></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-b00e2fdd><span class="visually-hidden" id="sidebar-aria-label" data-v-b00e2fdd> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="group" data-v-b00e2fdd><section class="VPSidebarItem level-0 collapsible" data-v-b00e2fdd data-v-e31bd47b><div class="item" role="button" tabindex="0" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><h2 class="text" data-v-e31bd47b>Flutter入门到实战</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-e31bd47b><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-e31bd47b><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-e31bd47b><!--[--><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter1/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第一章 初识Flutter</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter2/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第二章 简介</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter3/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第三章 基础组件</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter4/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第四章 布局类组件</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter5/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第五章 容器类Widget</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter6/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第六章 可滚动组件</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter7/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第七章 功能型Widget简介</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter8/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第八章 事件处理与通知</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter9/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第九章 动画</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter10/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第十章 自定义组件</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter11/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第十一章 文件操作</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter12/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第十二章 Flutter 扩展</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter13/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第十三章 多语言</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-e31bd47b data-v-e31bd47b><div class="item" data-v-e31bd47b><div class="indicator" data-v-e31bd47b></div><a class="VPLink link link" href="./largeFrontEnd/flutter/chapter14/index.html" data-v-e31bd47b><!--[--><p class="text" data-v-e31bd47b>第十四章 高级进阶</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-1919c326 data-v-669faec9><div class="VPDoc has-sidebar has-aside" data-v-669faec9 data-v-6b87e69f><!--[--><!--]--><div class="container" data-v-6b87e69f><div class="aside" data-v-6b87e69f><div class="aside-curtain" data-v-6b87e69f></div><div class="aside-container" data-v-6b87e69f><div class="aside-content" data-v-6b87e69f><div class="VPDocAside" data-v-6b87e69f data-v-3f215769><!--[--><!--]--><!--[--><!--]--><div class="VPDocAsideOutline" role="navigation" data-v-3f215769 data-v-6ae8e080><div class="content" data-v-6ae8e080><div class="outline-marker" data-v-6ae8e080></div><div class="outline-title" role="heading" data-v-6ae8e080>On this page</div><nav aria-labelledby="doc-outline-aria-label" data-v-6ae8e080><span class="visually-hidden" id="doc-outline-aria-label" data-v-6ae8e080> Table of Contents for current page </span><ul class="root" data-v-6ae8e080 data-v-d0ee3533><!--[--><!--]--></ul></nav></div></div><!--[--><!--]--><div class="spacer" data-v-3f215769></div><!--[--><!--]--><div class="VPDocAsideCarbonAds" data-v-3f215769><div class="VPCarbonAds" data-v-2e1efd59></div></div><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-6b87e69f><div class="content-container" data-v-6b87e69f><!--[--><!--]--><!----><main class="main" data-v-6b87e69f><div style="position:relative;" class="vp-doc _largeFrontEnd_flutter_chapter14_flutter_app_startup" data-v-6b87e69f><div><h1 id="_14-3-flutter启动流程和渲染管线" tabindex="-1">14.3 Flutter启动流程和渲染管线 <a class="header-anchor" href="#_14-3-flutter启动流程和渲染管线" aria-label="Permalink to &quot;14.3 Flutter启动流程和渲染管线&quot;">​</a></h1><p>本节我们会先介绍一下Flutter的启动流程，然后再介绍一下 Flutter 的 rendering pipeline (渲染管线)。</p><h2 id="_14-3-1-应用启动" tabindex="-1">14.3.1 应用启动 <a class="header-anchor" href="#_14-3-1-应用启动" aria-label="Permalink to &quot;14.3.1 应用启动&quot;">​</a></h2><p>Flutter的入口在&quot;lib/main.dart&quot;的<code>main()</code>函数中，它是Dart应用程序的起点。在Flutter应用中，<code>main()</code>函数最简单的实现如下：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">main</span><span style="color:#E1E4E8;">() </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">runApp</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">MyApp</span><span style="color:#E1E4E8;">());</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">main</span><span style="color:#24292E;">() </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">runApp</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">MyApp</span><span style="color:#24292E;">());</span></span></code></pre></div><p>可以看<code>main()</code>函数只调用了一个<code>runApp()</code>方法，我们看看<code>runApp()</code>方法中都做了什么：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">runApp</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">Widget</span><span style="color:#E1E4E8;"> app) {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">WidgetsFlutterBinding</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">ensureInitialized</span><span style="color:#E1E4E8;">()</span></span>
<span class="line"><span style="color:#E1E4E8;">    ..</span><span style="color:#B392F0;">attachRootWidget</span><span style="color:#E1E4E8;">(app)</span></span>
<span class="line"><span style="color:#E1E4E8;">    ..</span><span style="color:#B392F0;">scheduleWarmUpFrame</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">runApp</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">Widget</span><span style="color:#24292E;"> app) {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">WidgetsFlutterBinding</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">ensureInitialized</span><span style="color:#24292E;">()</span></span>
<span class="line"><span style="color:#24292E;">    ..</span><span style="color:#6F42C1;">attachRootWidget</span><span style="color:#24292E;">(app)</span></span>
<span class="line"><span style="color:#24292E;">    ..</span><span style="color:#6F42C1;">scheduleWarmUpFrame</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>参数<code>app</code>是一个 widget，它是 Flutter 应用启动后要展示的第一个组件。而<code>WidgetsFlutterBinding</code>正是绑定widget 框架和Flutter 引擎的桥梁，定义如下：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">class</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">WidgetsFlutterBinding</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">extends</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">BindingBase</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">with</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">GestureBinding</span><span style="color:#E1E4E8;">, </span><span style="color:#79B8FF;">ServicesBinding</span><span style="color:#E1E4E8;">, </span><span style="color:#79B8FF;">SchedulerBinding</span><span style="color:#E1E4E8;">, </span><span style="color:#79B8FF;">PaintingBinding</span><span style="color:#E1E4E8;">, </span><span style="color:#79B8FF;">SemanticsBinding</span><span style="color:#E1E4E8;">, </span><span style="color:#79B8FF;">RendererBinding</span><span style="color:#E1E4E8;">, </span><span style="color:#79B8FF;">WidgetsBinding</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">static</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">WidgetsBinding</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">ensureInitialized</span><span style="color:#E1E4E8;">() {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (</span><span style="color:#79B8FF;">WidgetsBinding</span><span style="color:#E1E4E8;">.instance </span><span style="color:#F97583;">==</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">)</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#79B8FF;">WidgetsFlutterBinding</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">WidgetsBinding</span><span style="color:#E1E4E8;">.instance;</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">class</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">WidgetsFlutterBinding</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">extends</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">BindingBase</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">with</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">GestureBinding</span><span style="color:#24292E;">, </span><span style="color:#005CC5;">ServicesBinding</span><span style="color:#24292E;">, </span><span style="color:#005CC5;">SchedulerBinding</span><span style="color:#24292E;">, </span><span style="color:#005CC5;">PaintingBinding</span><span style="color:#24292E;">, </span><span style="color:#005CC5;">SemanticsBinding</span><span style="color:#24292E;">, </span><span style="color:#005CC5;">RendererBinding</span><span style="color:#24292E;">, </span><span style="color:#005CC5;">WidgetsBinding</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">static</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">WidgetsBinding</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">ensureInitialized</span><span style="color:#24292E;">() {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (</span><span style="color:#005CC5;">WidgetsBinding</span><span style="color:#24292E;">.instance </span><span style="color:#D73A49;">==</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">)</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#005CC5;">WidgetsFlutterBinding</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">WidgetsBinding</span><span style="color:#24292E;">.instance;</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>可以看到<code>WidgetsFlutterBinding</code>继承自<code>BindingBase</code> 并混入了很多<code>Binding</code>，在介绍这些<code>Binding</code>之前我们先介绍一下<code>Window</code>，下面是<code>Window</code>的官方解释：</p><blockquote><p>The most basic interface to the host operating system&#39;s user interface.</p></blockquote><p>很明显，<code>Window</code> 正是 Flutter Framework 连接宿主操作系统的接口。我们看一下 <code>Window </code>类的部分定义：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">class</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Window</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 当前设备的DPI，即一个逻辑像素显示多少物理像素，数字越大，显示效果就越精细保真。</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// DPI是设备屏幕的固件属性，如Nexus 6的屏幕DPI为3.5 </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">double</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">get</span><span style="color:#E1E4E8;"> devicePixelRatio </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> _devicePixelRatio;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// Flutter UI绘制区域的大小</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">Size</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">get</span><span style="color:#E1E4E8;"> physicalSize </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> _physicalSize;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 当前系统默认的语言Locale</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">Locale</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">get</span><span style="color:#E1E4E8;"> locale;</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 当前系统字体缩放比例。  </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">double</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">get</span><span style="color:#E1E4E8;"> textScaleFactor </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> _textScaleFactor;  </span></span>
<span class="line"><span style="color:#E1E4E8;">    </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 当绘制区域大小改变回调</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">VoidCallback</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">get</span><span style="color:#E1E4E8;"> onMetricsChanged </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> _onMetricsChanged;  </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// Locale发生变化回调</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">VoidCallback</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">get</span><span style="color:#E1E4E8;"> onLocaleChanged </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> _onLocaleChanged;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 系统字体缩放变化回调</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">VoidCallback</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">get</span><span style="color:#E1E4E8;"> onTextScaleFactorChanged </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> _onTextScaleFactorChanged;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 绘制前回调，一般会受显示器的垂直同步信号VSync驱动，当屏幕刷新时就会被调用</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">FrameCallback</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">get</span><span style="color:#E1E4E8;"> onBeginFrame </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> _onBeginFrame;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 绘制回调  </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">VoidCallback</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">get</span><span style="color:#E1E4E8;"> onDrawFrame </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> _onDrawFrame;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 点击或指针事件回调</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">PointerDataPacketCallback</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">get</span><span style="color:#E1E4E8;"> onPointerDataPacket </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> _onPointerDataPacket;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 调度Frame，该方法执行后，onBeginFrame和onDrawFrame将紧接着会在合适时机被调用，</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 此方法会直接调用Flutter engine的Window_scheduleFrame方法</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">scheduleFrame</span><span style="color:#E1E4E8;">() </span><span style="color:#F97583;">native</span><span style="color:#E1E4E8;"> </span><span style="color:#9ECBFF;">&#39;Window_scheduleFrame&#39;</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 更新应用在GPU上的渲染,此方法会直接调用Flutter engine的Window_render方法</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">render</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">Scene</span><span style="color:#E1E4E8;"> scene) </span><span style="color:#F97583;">native</span><span style="color:#E1E4E8;"> </span><span style="color:#9ECBFF;">&#39;Window_render&#39;</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 发送平台消息</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">sendPlatformMessage</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">String</span><span style="color:#E1E4E8;"> name,</span></span>
<span class="line"><span style="color:#E1E4E8;">                           </span><span style="color:#79B8FF;">ByteData</span><span style="color:#E1E4E8;"> data,</span></span>
<span class="line"><span style="color:#E1E4E8;">                           </span><span style="color:#79B8FF;">PlatformMessageResponseCallback</span><span style="color:#E1E4E8;"> callback) ;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">// 平台通道消息处理回调  </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">PlatformMessageCallback</span><span style="color:#E1E4E8;"> </span><span style="color:#F97583;">get</span><span style="color:#E1E4E8;"> onPlatformMessage </span><span style="color:#F97583;">=&gt;</span><span style="color:#E1E4E8;"> _onPlatformMessage;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span></span>
<span class="line"><span style="color:#E1E4E8;">  ... </span><span style="color:#6A737D;">//其他属性及回调</span></span>
<span class="line"><span style="color:#E1E4E8;">   </span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">class</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Window</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">    </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 当前设备的DPI，即一个逻辑像素显示多少物理像素，数字越大，显示效果就越精细保真。</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// DPI是设备屏幕的固件属性，如Nexus 6的屏幕DPI为3.5 </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">double</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">get</span><span style="color:#24292E;"> devicePixelRatio </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> _devicePixelRatio;</span></span>
<span class="line"><span style="color:#24292E;">  </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// Flutter UI绘制区域的大小</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">Size</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">get</span><span style="color:#24292E;"> physicalSize </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> _physicalSize;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 当前系统默认的语言Locale</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">Locale</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">get</span><span style="color:#24292E;"> locale;</span></span>
<span class="line"><span style="color:#24292E;">    </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 当前系统字体缩放比例。  </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">double</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">get</span><span style="color:#24292E;"> textScaleFactor </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> _textScaleFactor;  </span></span>
<span class="line"><span style="color:#24292E;">    </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 当绘制区域大小改变回调</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">VoidCallback</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">get</span><span style="color:#24292E;"> onMetricsChanged </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> _onMetricsChanged;  </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// Locale发生变化回调</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">VoidCallback</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">get</span><span style="color:#24292E;"> onLocaleChanged </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> _onLocaleChanged;</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 系统字体缩放变化回调</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">VoidCallback</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">get</span><span style="color:#24292E;"> onTextScaleFactorChanged </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> _onTextScaleFactorChanged;</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 绘制前回调，一般会受显示器的垂直同步信号VSync驱动，当屏幕刷新时就会被调用</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">FrameCallback</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">get</span><span style="color:#24292E;"> onBeginFrame </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> _onBeginFrame;</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 绘制回调  </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">VoidCallback</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">get</span><span style="color:#24292E;"> onDrawFrame </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> _onDrawFrame;</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 点击或指针事件回调</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">PointerDataPacketCallback</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">get</span><span style="color:#24292E;"> onPointerDataPacket </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> _onPointerDataPacket;</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 调度Frame，该方法执行后，onBeginFrame和onDrawFrame将紧接着会在合适时机被调用，</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 此方法会直接调用Flutter engine的Window_scheduleFrame方法</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">scheduleFrame</span><span style="color:#24292E;">() </span><span style="color:#D73A49;">native</span><span style="color:#24292E;"> </span><span style="color:#032F62;">&#39;Window_scheduleFrame&#39;</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 更新应用在GPU上的渲染,此方法会直接调用Flutter engine的Window_render方法</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">render</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">Scene</span><span style="color:#24292E;"> scene) </span><span style="color:#D73A49;">native</span><span style="color:#24292E;"> </span><span style="color:#032F62;">&#39;Window_render&#39;</span><span style="color:#24292E;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 发送平台消息</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">sendPlatformMessage</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">String</span><span style="color:#24292E;"> name,</span></span>
<span class="line"><span style="color:#24292E;">                           </span><span style="color:#005CC5;">ByteData</span><span style="color:#24292E;"> data,</span></span>
<span class="line"><span style="color:#24292E;">                           </span><span style="color:#005CC5;">PlatformMessageResponseCallback</span><span style="color:#24292E;"> callback) ;</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">// 平台通道消息处理回调  </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">PlatformMessageCallback</span><span style="color:#24292E;"> </span><span style="color:#D73A49;">get</span><span style="color:#24292E;"> onPlatformMessage </span><span style="color:#D73A49;">=&gt;</span><span style="color:#24292E;"> _onPlatformMessage;</span></span>
<span class="line"><span style="color:#24292E;">  </span></span>
<span class="line"><span style="color:#24292E;">  ... </span><span style="color:#6A737D;">//其他属性及回调</span></span>
<span class="line"><span style="color:#24292E;">   </span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>可以看到<code>Window</code>类包含了当前设备和系统的一些信息以及Flutter Engine的一些回调。现在我们再回来看看<code>WidgetsFlutterBinding</code>混入的各种Binding。通过查看这些 Binding的源码，我们可以发现这些Binding中基本都是监听并处理<code>Window</code>对象的一些事件，然后将这些事件按照Framework的模型包装、抽象然后分发。可以看到<code>WidgetsFlutterBinding</code>正是粘连Flutter engine与上层Framework的“胶水”。</p><ul><li><code>GestureBinding</code>：提供了<code>window.onPointerDataPacket</code> 回调，绑定Framework手势子系统，是Framework事件模型与底层事件的绑定入口。</li><li><code>ServicesBinding</code>：提供了<code>window.onPlatformMessage</code> 回调， 用于绑定平台消息通道（message channel），主要处理原生和Flutter通信。</li><li><code>SchedulerBinding</code>：提供了<code>window.onBeginFrame</code>和<code>window.onDrawFrame</code>回调，监听刷新事件，绑定Framework绘制调度子系统。</li><li><code>PaintingBinding</code>：绑定绘制库，主要用于处理图片缓存。</li><li><code>SemanticsBinding</code>：语义化层与Flutter engine的桥梁，主要是辅助功能的底层支持。</li><li><code>RendererBinding</code>: 提供了<code>window.onMetricsChanged</code> 、<code>window.onTextScaleFactorChanged</code> 等回调。它是渲染树与Flutter engine的桥梁。</li><li><code>WidgetsBinding</code>：提供了<code>window.onLocaleChanged</code>、<code>onBuildScheduled </code> 等回调。它是Flutter widget层与engine的桥梁。</li></ul><p><code> WidgetsFlutterBinding.ensureInitialized()</code>负责初始化一个<code>WidgetsBinding</code>的全局单例，紧接着会调用<code>WidgetsBinding</code>的<code>attachRootWidget</code>方法，该方法负责将根Widget添加到<code>RenderView</code>上，代码如下：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">attachRootWidget</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">Widget</span><span style="color:#E1E4E8;"> rootWidget) {</span></span>
<span class="line"><span style="color:#E1E4E8;">  _renderViewElement </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">RenderObjectToWidgetAdapter</span><span style="color:#E1E4E8;">&lt;</span><span style="color:#79B8FF;">RenderBox</span><span style="color:#E1E4E8;">&gt;(</span></span>
<span class="line"><span style="color:#E1E4E8;">    container</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> renderView, </span></span>
<span class="line"><span style="color:#E1E4E8;">    debugShortDescription</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#9ECBFF;">&#39;[root]&#39;</span><span style="color:#E1E4E8;">,</span></span>
<span class="line"><span style="color:#E1E4E8;">    child</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> rootWidget</span></span>
<span class="line"><span style="color:#E1E4E8;">  ).</span><span style="color:#B392F0;">attachToRenderTree</span><span style="color:#E1E4E8;">(buildOwner, renderViewElement);</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">attachRootWidget</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">Widget</span><span style="color:#24292E;"> rootWidget) {</span></span>
<span class="line"><span style="color:#24292E;">  _renderViewElement </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">RenderObjectToWidgetAdapter</span><span style="color:#24292E;">&lt;</span><span style="color:#005CC5;">RenderBox</span><span style="color:#24292E;">&gt;(</span></span>
<span class="line"><span style="color:#24292E;">    container</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> renderView, </span></span>
<span class="line"><span style="color:#24292E;">    debugShortDescription</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#032F62;">&#39;[root]&#39;</span><span style="color:#24292E;">,</span></span>
<span class="line"><span style="color:#24292E;">    child</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> rootWidget</span></span>
<span class="line"><span style="color:#24292E;">  ).</span><span style="color:#6F42C1;">attachToRenderTree</span><span style="color:#24292E;">(buildOwner, renderViewElement);</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>注意，代码中的有<code>renderView</code>和<code>renderViewElement</code>两个变量，<code>renderView</code>是一个<code>RenderObject</code>，它是渲染树的根，而<code>renderViewElement</code>是<code>renderView</code>对应的<code>Element</code>对象，可见该方法主要完成了根widget到根 <code>RenderObject</code>再到根<code>Element</code>的整个关联过程。我们看看<code>attachToRenderTree</code>的源码实现：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#79B8FF;">RenderObjectToWidgetElement</span><span style="color:#E1E4E8;">&lt;</span><span style="color:#79B8FF;">T</span><span style="color:#E1E4E8;">&gt; </span><span style="color:#B392F0;">attachToRenderTree</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">BuildOwner</span><span style="color:#E1E4E8;"> owner, [</span><span style="color:#79B8FF;">RenderObjectToWidgetElement</span><span style="color:#E1E4E8;">&lt;</span><span style="color:#79B8FF;">T</span><span style="color:#E1E4E8;">&gt; element]) {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (element </span><span style="color:#F97583;">==</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    owner.</span><span style="color:#B392F0;">lockState</span><span style="color:#E1E4E8;">(() {</span></span>
<span class="line"><span style="color:#E1E4E8;">      element </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">createElement</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#F97583;">assert</span><span style="color:#E1E4E8;">(element </span><span style="color:#F97583;">!=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">);</span></span>
<span class="line"><span style="color:#E1E4E8;">      element.</span><span style="color:#B392F0;">assignOwner</span><span style="color:#E1E4E8;">(owner);</span></span>
<span class="line"><span style="color:#E1E4E8;">    });</span></span>
<span class="line"><span style="color:#E1E4E8;">    owner.</span><span style="color:#B392F0;">buildScope</span><span style="color:#E1E4E8;">(element, () {</span></span>
<span class="line"><span style="color:#E1E4E8;">      element.</span><span style="color:#B392F0;">mount</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">, </span><span style="color:#79B8FF;">null</span><span style="color:#E1E4E8;">);</span></span>
<span class="line"><span style="color:#E1E4E8;">    });</span></span>
<span class="line"><span style="color:#E1E4E8;">  } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">    element._newWidget </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">this</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">    element.</span><span style="color:#B392F0;">markNeedsBuild</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> element;</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#005CC5;">RenderObjectToWidgetElement</span><span style="color:#24292E;">&lt;</span><span style="color:#005CC5;">T</span><span style="color:#24292E;">&gt; </span><span style="color:#6F42C1;">attachToRenderTree</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">BuildOwner</span><span style="color:#24292E;"> owner, [</span><span style="color:#005CC5;">RenderObjectToWidgetElement</span><span style="color:#24292E;">&lt;</span><span style="color:#005CC5;">T</span><span style="color:#24292E;">&gt; element]) {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (element </span><span style="color:#D73A49;">==</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">) {</span></span>
<span class="line"><span style="color:#24292E;">    owner.</span><span style="color:#6F42C1;">lockState</span><span style="color:#24292E;">(() {</span></span>
<span class="line"><span style="color:#24292E;">      element </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">createElement</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#D73A49;">assert</span><span style="color:#24292E;">(element </span><span style="color:#D73A49;">!=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">);</span></span>
<span class="line"><span style="color:#24292E;">      element.</span><span style="color:#6F42C1;">assignOwner</span><span style="color:#24292E;">(owner);</span></span>
<span class="line"><span style="color:#24292E;">    });</span></span>
<span class="line"><span style="color:#24292E;">    owner.</span><span style="color:#6F42C1;">buildScope</span><span style="color:#24292E;">(element, () {</span></span>
<span class="line"><span style="color:#24292E;">      element.</span><span style="color:#6F42C1;">mount</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">null</span><span style="color:#24292E;">, </span><span style="color:#005CC5;">null</span><span style="color:#24292E;">);</span></span>
<span class="line"><span style="color:#24292E;">    });</span></span>
<span class="line"><span style="color:#24292E;">  } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">    element._newWidget </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">this</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">    element.</span><span style="color:#6F42C1;">markNeedsBuild</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> element;</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>该方法负责创建根element，即<code> RenderObjectToWidgetElement</code>，并且将element与widget 进行关联，即创建出 widget树对应的element树。如果element 已经创建过了，则将根element 中关联的widget 设为新的，由此可以看出element 只会创建一次，后面会进行复用。那么<code>BuildOwner</code>是什么呢？其实它就是widget framework的管理类，它跟踪哪些 widget 需要重新构建。</p><p>组件树在构建（build）完毕后，回到<code>runApp</code>的实现中，当调用完<code>attachRootWidget</code>后，最后一行会调用 <code>WidgetsFlutterBinding</code> 实例的 <code>scheduleWarmUpFrame()</code> 方法，该方法的实现在<code>SchedulerBinding</code> 中，它被调用后会立即进行一次绘制，在此次绘制结束前，该方法会锁定事件分发，也就是说在本次绘制结束完成之前 Flutter 将不会响应各种事件，这可以保证在绘制过程中不会再触发新的重绘。</p><h2 id="_14-3-2-渲染管线" tabindex="-1">14.3.2 渲染管线 <a class="header-anchor" href="#_14-3-2-渲染管线" aria-label="Permalink to &quot;14.3.2 渲染管线&quot;">​</a></h2><h3 id="_1-frame" tabindex="-1">1. Frame <a class="header-anchor" href="#_1-frame" aria-label="Permalink to &quot;1. Frame&quot;">​</a></h3><p>一次绘制过程，我们称其为一帧（frame）。我们之前说的 Flutter 可以实现60fps（Frame Per-Second）就是指一秒钟最多可以触发 60 次重绘，FPS 值越大，界面就越流畅。这里需要说明的是 Flutter中 的 frame 概念并不等同于屏幕刷新帧（frame），因为Flutter UI 框架的 frame 并不是每次屏幕刷新都会触发，这是因为，如果 UI 在一段时间不变，那么每次屏幕刷新都重新走一遍渲染流程是不必要的，因此，Flutter 在第一帧渲染结束后会采取一种主动请求 frame 的方式来实现只有当UI可能会改变时才会重新走渲染流程。</p><ol><li>Flutter 在 <code>window</code> 上注册一个 <code>onBeginFrame </code>和一个 <code>onDrawFrame</code> 回调，在<code>onDrawFrame</code> 回调中最终会调用 <code>drawFrame</code>。</li><li>当我们调用 <code>window.scheduleFrame()</code> 方法之后，Flutter引擎会在合适的时机（可以认为是在屏幕下一次刷新之前，具体取决于Flutter引擎的实现）来调用<code>onBeginFrame </code>和<code>onDrawFrame</code>。</li></ol><p>可以看见，只有主动调用<code>scheduleFrame() </code>，才会执行 <code>drawFrame</code>。所以，<strong>我们在Flutter 中的提到 frame 时，如无特别说明，则是和 <code>drawFrame()</code> 的调用对应，而不是和屏幕的刷新频率对应</strong>。</p><h3 id="_2-flutter-调度过程-schedulerphase" tabindex="-1">2. Flutter 调度过程 SchedulerPhase <a class="header-anchor" href="#_2-flutter-调度过程-schedulerphase" aria-label="Permalink to &quot;2. Flutter 调度过程 SchedulerPhase&quot;">​</a></h3><p>Flutter 应用执行过程简单来讲分为 idle 和 frame 两种状态，idle 状态代表没有 frame 处理，如果应用状态改变需要刷新 UI，则需要通过<code>scheduleFrame()</code>去请求新的 frame，当 frame 到来时，就进入了frame状态，整个Flutter应用生命周期就是在 idle 和 frame 两种状态间切换。</p><h4 id="frame-处理流程" tabindex="-1">frame 处理流程 <a class="header-anchor" href="#frame-处理流程" aria-label="Permalink to &quot;frame 处理流程&quot;">​</a></h4><p>当有新的 frame 到来时，具体处理过程就是依次执行四个任务队列：transientCallbacks、midFrameMicrotasks、persistentCallbacks、postFrameCallbacks，当四个任务队列执行完毕后当前 frame 结束。综上，Flutter 将整个生命周期分为五种状态，通过 SchedulerPhase 枚举类来表示它们：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">enum</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">SchedulerPhase</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">/// 空闲状态，并没有 frame 在处理。这种状态代表页面未发生变化，并不需要重新渲染。</span></span>
<span class="line"><span style="color:#6A737D;">  /// 如果页面发生变化，需要调用</span><span style="color:#E1E4E8;">`scheduleFrame()`</span><span style="color:#6A737D;">来请求 frame。</span></span>
<span class="line"><span style="color:#6A737D;">  /// 注意，空闲状态只是指没有 frame 在处理，通常微任务、定时器回调或者用户事件回调都</span></span>
<span class="line"><span style="color:#6A737D;">  /// 可能被执行，比如监听了tap事件，用户点击后我们 onTap 回调就是在idle阶段被执行的。</span></span>
<span class="line"><span style="color:#E1E4E8;">  idle,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">/// 执行”临时“回调任务，”临时“回调任务只能被执行一次，执行后会被移出”临时“任务队列。</span></span>
<span class="line"><span style="color:#6A737D;">  /// 典型的代表就是动画回调会在该阶段执行。</span></span>
<span class="line"><span style="color:#E1E4E8;">  transientCallbacks,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">/// 在执行临时任务时可能会产生一些新的微任务，比如在执行第一个临时任务时创建了一个</span></span>
<span class="line"><span style="color:#6A737D;">  /// Future，且这个 Future 在所有临时任务执行完毕前就已经 resolve 了，这中情况</span></span>
<span class="line"><span style="color:#6A737D;">  /// Future 的回调将在</span><span style="color:#FFAB70;">[midFrameMicrotasks]</span><span style="color:#6A737D;">阶段执行</span></span>
<span class="line"><span style="color:#E1E4E8;">  midFrameMicrotasks,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">/// 执行一些持久的任务（每一个frame都要执行的任务），比如渲染管线（构建、布局、绘制）</span></span>
<span class="line"><span style="color:#6A737D;">  /// 就是在该任务队列中执行的.</span></span>
<span class="line"><span style="color:#E1E4E8;">  persistentCallbacks,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">/// 在当前 frame 在结束之前将会执行 postFrameCallbacks，通常进行一些清理工作和</span></span>
<span class="line"><span style="color:#6A737D;">  /// 请求新的 frame。</span></span>
<span class="line"><span style="color:#E1E4E8;">  postFrameCallbacks,</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">enum</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">SchedulerPhase</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">  </span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">/// 空闲状态，并没有 frame 在处理。这种状态代表页面未发生变化，并不需要重新渲染。</span></span>
<span class="line"><span style="color:#6A737D;">  /// 如果页面发生变化，需要调用</span><span style="color:#24292E;">`scheduleFrame()`</span><span style="color:#6A737D;">来请求 frame。</span></span>
<span class="line"><span style="color:#6A737D;">  /// 注意，空闲状态只是指没有 frame 在处理，通常微任务、定时器回调或者用户事件回调都</span></span>
<span class="line"><span style="color:#6A737D;">  /// 可能被执行，比如监听了tap事件，用户点击后我们 onTap 回调就是在idle阶段被执行的。</span></span>
<span class="line"><span style="color:#24292E;">  idle,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">/// 执行”临时“回调任务，”临时“回调任务只能被执行一次，执行后会被移出”临时“任务队列。</span></span>
<span class="line"><span style="color:#6A737D;">  /// 典型的代表就是动画回调会在该阶段执行。</span></span>
<span class="line"><span style="color:#24292E;">  transientCallbacks,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">/// 在执行临时任务时可能会产生一些新的微任务，比如在执行第一个临时任务时创建了一个</span></span>
<span class="line"><span style="color:#6A737D;">  /// Future，且这个 Future 在所有临时任务执行完毕前就已经 resolve 了，这中情况</span></span>
<span class="line"><span style="color:#6A737D;">  /// Future 的回调将在</span><span style="color:#E36209;">[midFrameMicrotasks]</span><span style="color:#6A737D;">阶段执行</span></span>
<span class="line"><span style="color:#24292E;">  midFrameMicrotasks,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">/// 执行一些持久的任务（每一个frame都要执行的任务），比如渲染管线（构建、布局、绘制）</span></span>
<span class="line"><span style="color:#6A737D;">  /// 就是在该任务队列中执行的.</span></span>
<span class="line"><span style="color:#24292E;">  persistentCallbacks,</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">/// 在当前 frame 在结束之前将会执行 postFrameCallbacks，通常进行一些清理工作和</span></span>
<span class="line"><span style="color:#6A737D;">  /// 请求新的 frame。</span></span>
<span class="line"><span style="color:#24292E;">  postFrameCallbacks,</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>需要注意，我们接下来要重点介绍的渲染管线就是在 persistentCallbacks 中执行的。</p><h3 id="_3-渲染管线-rendering-pipeline" tabindex="-1">3. 渲染管线（rendering pipeline） <a class="header-anchor" href="#_3-渲染管线-rendering-pipeline" aria-label="Permalink to &quot;3. 渲染管线（rendering pipeline）&quot;">​</a></h3><p>当新的 frame 到来时，调用到 WidgetsBinding 的 <code>drawFrame()</code> 方法，我们来看看它的实现：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">drawFrame</span><span style="color:#E1E4E8;">() {</span></span>
<span class="line"><span style="color:#E1E4E8;"> ...</span><span style="color:#6A737D;">//省略无关代码</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">try</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">    buildOwner.</span><span style="color:#B392F0;">buildScope</span><span style="color:#E1E4E8;">(renderViewElement); </span><span style="color:#6A737D;">// 先执行构建</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#79B8FF;">super</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">drawFrame</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">//然后调用父类的 drawFrame 方法</span></span>
<span class="line"><span style="color:#E1E4E8;">  } </span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">drawFrame</span><span style="color:#24292E;">() {</span></span>
<span class="line"><span style="color:#24292E;"> ...</span><span style="color:#6A737D;">//省略无关代码</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">try</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">    buildOwner.</span><span style="color:#6F42C1;">buildScope</span><span style="color:#24292E;">(renderViewElement); </span><span style="color:#6A737D;">// 先执行构建</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#005CC5;">super</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">drawFrame</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">//然后调用父类的 drawFrame 方法</span></span>
<span class="line"><span style="color:#24292E;">  } </span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>实际上关键的代码就两行：先重新构建（build），然后再调用父类的 drawFrame 方法，我们将父类的 drawFrame方法展开后：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">drawFrame</span><span style="color:#E1E4E8;">() {</span></span>
<span class="line"><span style="color:#E1E4E8;">  buildOwner</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">buildScope</span><span style="color:#E1E4E8;">(renderViewElement</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">); </span><span style="color:#6A737D;">// 1.重新构建widget树</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#6A737D;">//下面是 展开 super.drawFrame() 方法</span></span>
<span class="line"><span style="color:#E1E4E8;">  pipelineOwner.</span><span style="color:#B392F0;">flushLayout</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">// 2.更新布局</span></span>
<span class="line"><span style="color:#E1E4E8;">  pipelineOwner.</span><span style="color:#B392F0;">flushCompositingBits</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">//3.更新“层合成”信息</span></span>
<span class="line"><span style="color:#E1E4E8;">  pipelineOwner.</span><span style="color:#B392F0;">flushPaint</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">// 4.重绘</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (sendFramesToEngine) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    renderView.</span><span style="color:#B392F0;">compositeFrame</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">// 5. 上屏，会将绘制出的bit数据发送给GPU</span></span>
<span class="line"><span style="color:#E1E4E8;">    ...</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">drawFrame</span><span style="color:#24292E;">() {</span></span>
<span class="line"><span style="color:#24292E;">  buildOwner</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">buildScope</span><span style="color:#24292E;">(renderViewElement</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">); </span><span style="color:#6A737D;">// 1.重新构建widget树</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6A737D;">//下面是 展开 super.drawFrame() 方法</span></span>
<span class="line"><span style="color:#24292E;">  pipelineOwner.</span><span style="color:#6F42C1;">flushLayout</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">// 2.更新布局</span></span>
<span class="line"><span style="color:#24292E;">  pipelineOwner.</span><span style="color:#6F42C1;">flushCompositingBits</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">//3.更新“层合成”信息</span></span>
<span class="line"><span style="color:#24292E;">  pipelineOwner.</span><span style="color:#6F42C1;">flushPaint</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">// 4.重绘</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (sendFramesToEngine) {</span></span>
<span class="line"><span style="color:#24292E;">    renderView.</span><span style="color:#6F42C1;">compositeFrame</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">// 5. 上屏，会将绘制出的bit数据发送给GPU</span></span>
<span class="line"><span style="color:#24292E;">    ...</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>可以看到主要做了5件事：</p><ol><li><p>重新构建widget树。</p></li><li><p>更新布局。</p></li><li><p>更新“层合成”信息。</p></li><li><p>重绘。</p></li><li><p>上屏：将绘制的产物显示在屏幕上</p></li></ol><p>我们称上面的5步为 rendering pipeline，中文翻译为 “渲染流水线” 或 “渲染管线”。而渲染管线的这 5 个步骤的具体过程便是本章重点要介绍的。下面我们以 setState 的执行更新的流程为例先对整个更新流程有一个大概的影响</p><h3 id="_4-setstate-执行流" tabindex="-1">4. setState 执行流 <a class="header-anchor" href="#_4-setstate-执行流" aria-label="Permalink to &quot;4. setState 执行流&quot;">​</a></h3><p>setState 调用后：</p><ol><li>首先调用当前 element 的 markNeedsBuild 方法，将当前 element标记为 dirty 。</li><li>接着调用 scheduleBuildFor，将当前 element 添加到pipelineOwner的 dirtyElements 列表。</li><li>最后请求一个新的 frame，随后会绘制新的 frame：onBuildScheduled-&gt;ensureVisualUpdate-&gt;scheduleFrame() 。当新的 frame 到来时执行渲染管线</li></ol><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">drawFrame</span><span style="color:#E1E4E8;">() {</span></span>
<span class="line"><span style="color:#E1E4E8;">  buildOwner</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">.</span><span style="color:#B392F0;">buildScope</span><span style="color:#E1E4E8;">(renderViewElement</span><span style="color:#F97583;">!</span><span style="color:#E1E4E8;">); </span><span style="color:#6A737D;">//重新构建widget树</span></span>
<span class="line"><span style="color:#E1E4E8;">  pipelineOwner.</span><span style="color:#B392F0;">flushLayout</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">// 更新布局</span></span>
<span class="line"><span style="color:#E1E4E8;">  pipelineOwner.</span><span style="color:#B392F0;">flushCompositingBits</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">//更新合成信息</span></span>
<span class="line"><span style="color:#E1E4E8;">  pipelineOwner.</span><span style="color:#B392F0;">flushPaint</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">// 更新绘制</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (sendFramesToEngine) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    renderView.</span><span style="color:#B392F0;">compositeFrame</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">// 上屏，会将绘制出的bit数据发送给GPU</span></span>
<span class="line"><span style="color:#E1E4E8;">    pipelineOwner.</span><span style="color:#B392F0;">flushSemantics</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">// this also sends the semantics to the OS.</span></span>
<span class="line"><span style="color:#E1E4E8;">    _firstFrameSent </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">true</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">drawFrame</span><span style="color:#24292E;">() {</span></span>
<span class="line"><span style="color:#24292E;">  buildOwner</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">.</span><span style="color:#6F42C1;">buildScope</span><span style="color:#24292E;">(renderViewElement</span><span style="color:#D73A49;">!</span><span style="color:#24292E;">); </span><span style="color:#6A737D;">//重新构建widget树</span></span>
<span class="line"><span style="color:#24292E;">  pipelineOwner.</span><span style="color:#6F42C1;">flushLayout</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">// 更新布局</span></span>
<span class="line"><span style="color:#24292E;">  pipelineOwner.</span><span style="color:#6F42C1;">flushCompositingBits</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">//更新合成信息</span></span>
<span class="line"><span style="color:#24292E;">  pipelineOwner.</span><span style="color:#6F42C1;">flushPaint</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">// 更新绘制</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (sendFramesToEngine) {</span></span>
<span class="line"><span style="color:#24292E;">    renderView.</span><span style="color:#6F42C1;">compositeFrame</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">// 上屏，会将绘制出的bit数据发送给GPU</span></span>
<span class="line"><span style="color:#24292E;">    pipelineOwner.</span><span style="color:#6F42C1;">flushSemantics</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">// this also sends the semantics to the OS.</span></span>
<span class="line"><span style="color:#24292E;">    _firstFrameSent </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">true</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><ol><li>重新构建 widget 树：如果 dirtyElements 列表不为空，则遍历该列表，调用每一个element的rebuild方法重新构建新的widget（树），由于新的widget(树)使用新的状态构建，所以可能导致widget布局信息（占用的空间和位置）发生变化，如果发生变化，则会调用其renderObject的markNeedsLayout方法，该方法会从当前节点向父级查找，直到找到一个relayoutBoundary的节点，然后会将它添加到一个全局的nodesNeedingLayout列表中；如果直到根节点也没有找到relayoutBoundary，则将根节点添加到nodesNeedingLayout列表中。</li><li>更新布局：遍历nodesNeedingLayout数组，对每一个renderObject重新布局（调用其layout方法），确定新的大小和偏移。layout方法中会调用markNeedsPaint()，该方法和 markNeedsLayout 方法功能类似，也会从当前节点向父级查找，直到找到一个isRepaintBoundary属性为true的父节点，然后将它添加到一个全局的nodesNeedingPaint列表中；由于根节点（RenderView）的 isRepaintBoundary 为 true，所以必会找到一个。查找过程结束后会调用 buildOwner.requestVisualUpdate 方法，该方法最终会调用scheduleFrame()，该方法中会先判断是否已经请求过新的frame，如果没有则请求一个新的frame。</li><li>更新合成信息：先忽略，我们在14.8节专门介绍。</li><li>更新绘制：遍历nodesNeedingPaint列表，调用每一个节点的paint方法进行重绘，绘制过程会生成Layer。需要说明一下，flutter中绘制结果是保存在Layer中的，也就是说只要Layer不释放，那么绘制的结果就会被缓存，因此，Layer可以跨frame来缓存绘制结果，避免不必要的重绘开销。Flutter框架绘制过程中，遇到isRepaintBoundary 为 true 的节点时，才会生成一个新的Layer。可见Layer和 renderObject 不是一一对应关系，父子节点可以共享，这个我们会在随后的一个试验中来验证。当然，如果是自定义组件，我们可以在renderObject中手动添加任意多个 Layer，这通常用于只需一次绘制而随后不会发生变化的绘制元素的缓存场景，这个随后我们也会通过一个例子来演示。</li><li>上屏：绘制完成后，我们得到的是一棵Layer树，最后我们需要将Layer树中的绘制信息在屏幕上显示。我们知道Flutter是自实现的渲染引擎，因此，我们需要将绘制信息提交给Flutter engine，而<code>renderView.compositeFrame</code> 正是完成了这个使命。</li></ol><p>以上，便是setState被调用到UI更的大概更新过程，实际的流程会更复杂一些，比如在build过程中是不允许再调用setState的，框架需要做一些检查。又比如在frame中会涉及到动画的调度、在上屏时会将所有的Layer添加到场景（Scene）对象后，再渲染Scene。上面的流程读者先有个印象即可，我们将在后面的小节中详细介绍。</p><h3 id="_5-setstate-执行时机问题" tabindex="-1">5. setState 执行时机问题 <a class="header-anchor" href="#_5-setstate-执行时机问题" aria-label="Permalink to &quot;5. setState 执行时机问题&quot;">​</a></h3><p>setState 会触发 build，而 build 是在执行 <code>persistentCallbacks</code> 阶段执行的，因此只要不是在该阶段执行 setState 就绝对安全，但是这样的粒度太粗，比如在transientCallbacks 和 midFrameMicrotasks 阶段，如果应用状态发生变化，最好的方式是只将组件标记为 dirty，而不用再去请求新的 frame ，因为当前frame 还没有执行到 <code>persistentCallbacks</code>，因此后面执行到后就会在当前帧渲染管线中刷新UI。因此，setState 在标记完 dirty 后会先判断一下调度状态，如果是 idle 或 执行 postFrameCallbacks 阶段才会去请求新的 frame :</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">ensureVisualUpdate</span><span style="color:#E1E4E8;">() {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">switch</span><span style="color:#E1E4E8;"> (schedulerPhase) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">case</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">SchedulerPhase</span><span style="color:#E1E4E8;">.idle</span><span style="color:#F97583;">:</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">case</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">SchedulerPhase</span><span style="color:#E1E4E8;">.postFrameCallbacks</span><span style="color:#F97583;">:</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#B392F0;">scheduleFrame</span><span style="color:#E1E4E8;">(); </span><span style="color:#6A737D;">// 请求新的frame</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">case</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">SchedulerPhase</span><span style="color:#E1E4E8;">.transientCallbacks</span><span style="color:#F97583;">:</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">case</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">SchedulerPhase</span><span style="color:#E1E4E8;">.midFrameMicrotasks</span><span style="color:#F97583;">:</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">case</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">SchedulerPhase</span><span style="color:#E1E4E8;">.persistentCallbacks</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> </span><span style="color:#6A737D;">// 注意这一行</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;">;</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">ensureVisualUpdate</span><span style="color:#24292E;">() {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">switch</span><span style="color:#24292E;"> (schedulerPhase) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">case</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">SchedulerPhase</span><span style="color:#24292E;">.idle</span><span style="color:#D73A49;">:</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">case</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">SchedulerPhase</span><span style="color:#24292E;">.postFrameCallbacks</span><span style="color:#D73A49;">:</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#6F42C1;">scheduleFrame</span><span style="color:#24292E;">(); </span><span style="color:#6A737D;">// 请求新的frame</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#D73A49;">return</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">case</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">SchedulerPhase</span><span style="color:#24292E;">.transientCallbacks</span><span style="color:#D73A49;">:</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">case</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">SchedulerPhase</span><span style="color:#24292E;">.midFrameMicrotasks</span><span style="color:#D73A49;">:</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">case</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">SchedulerPhase</span><span style="color:#24292E;">.persistentCallbacks</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> </span><span style="color:#6A737D;">// 注意这一行</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#D73A49;">return</span><span style="color:#24292E;">;</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>上面的代码在大多数情况下是没有问题的，但是如果我们在 build 阶段又调用 setState 的话还是会有问题，因为如果我们在 build 阶段又调用 setState 的话就又会导致 build....这样将将导致循环调用，因此 flutter 框架发现在 build 阶段调用 setState 的话就会报错，如：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">Widget</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">build</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">BuildContext</span><span style="color:#E1E4E8;"> context) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">LayoutBuilder</span><span style="color:#E1E4E8;">(</span></span>
<span class="line"><span style="color:#E1E4E8;">      builder</span><span style="color:#F97583;">:</span><span style="color:#E1E4E8;"> (context, c) {</span></span>
<span class="line"><span style="color:#E1E4E8;">        </span><span style="color:#6A737D;">// build 阶段不能调用 setState, 会报错</span></span>
<span class="line"><span style="color:#E1E4E8;">        </span><span style="color:#B392F0;">setState</span><span style="color:#E1E4E8;">(() {</span></span>
<span class="line"><span style="color:#E1E4E8;">          </span><span style="color:#F97583;">++</span><span style="color:#E1E4E8;">index;</span></span>
<span class="line"><span style="color:#E1E4E8;">        });</span></span>
<span class="line"><span style="color:#E1E4E8;">        </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Text</span><span style="color:#E1E4E8;">(</span><span style="color:#9ECBFF;">&#39;xx&#39;</span><span style="color:#E1E4E8;">);</span></span>
<span class="line"><span style="color:#E1E4E8;">      },</span></span>
<span class="line"><span style="color:#E1E4E8;">    );</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">Widget</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">build</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">BuildContext</span><span style="color:#24292E;"> context) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">LayoutBuilder</span><span style="color:#24292E;">(</span></span>
<span class="line"><span style="color:#24292E;">      builder</span><span style="color:#D73A49;">:</span><span style="color:#24292E;"> (context, c) {</span></span>
<span class="line"><span style="color:#24292E;">        </span><span style="color:#6A737D;">// build 阶段不能调用 setState, 会报错</span></span>
<span class="line"><span style="color:#24292E;">        </span><span style="color:#6F42C1;">setState</span><span style="color:#24292E;">(() {</span></span>
<span class="line"><span style="color:#24292E;">          </span><span style="color:#D73A49;">++</span><span style="color:#24292E;">index;</span></span>
<span class="line"><span style="color:#24292E;">        });</span></span>
<span class="line"><span style="color:#24292E;">        </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Text</span><span style="color:#24292E;">(</span><span style="color:#032F62;">&#39;xx&#39;</span><span style="color:#24292E;">);</span></span>
<span class="line"><span style="color:#24292E;">      },</span></span>
<span class="line"><span style="color:#24292E;">    );</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span></code></pre></div><p>运行后会报错，控制台会打印：</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#e1e4e8;">==== Exception caught by widgets library ====</span></span>
<span class="line"><span style="color:#e1e4e8;">The following assertion was thrown building LayoutBuilder:</span></span>
<span class="line"><span style="color:#e1e4e8;">setState() or markNeedsBuild() called during build.</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#24292e;">==== Exception caught by widgets library ====</span></span>
<span class="line"><span style="color:#24292e;">The following assertion was thrown building LayoutBuilder:</span></span>
<span class="line"><span style="color:#24292e;">setState() or markNeedsBuild() called during build.</span></span></code></pre></div><p>需要注意，如果我们直接在 build 中调用<code>setState</code> ，代码如下：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">@override</span></span>
<span class="line"><span style="color:#79B8FF;">Widget</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">build</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">BuildContext</span><span style="color:#E1E4E8;"> context) {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#B392F0;">setState</span><span style="color:#E1E4E8;">(() {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#F97583;">++</span><span style="color:#E1E4E8;">index;</span></span>
<span class="line"><span style="color:#E1E4E8;">  });</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">return</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">Text</span><span style="color:#E1E4E8;">(</span><span style="color:#9ECBFF;">&#39;$</span><span style="color:#79B8FF;">index</span><span style="color:#9ECBFF;">&#39;</span><span style="color:#E1E4E8;">);</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">@override</span></span>
<span class="line"><span style="color:#005CC5;">Widget</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">build</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">BuildContext</span><span style="color:#24292E;"> context) {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#6F42C1;">setState</span><span style="color:#24292E;">(() {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#D73A49;">++</span><span style="color:#24292E;">index;</span></span>
<span class="line"><span style="color:#24292E;">  });</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">return</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">Text</span><span style="color:#24292E;">(</span><span style="color:#032F62;">&#39;$</span><span style="color:#005CC5;">index</span><span style="color:#032F62;">&#39;</span><span style="color:#24292E;">);</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>运行后是不会报错的，原因是在执行 build 时当前组件的 dirty 状态（对应的element中）为 true，只有 build 执行完后才会被置为 false。而 setState 执行的时候会会先判断当前 dirty 值，如果为 true 则会直接返回，因此就不会报错。</p><p>上面我们只讨论了在 build 阶段调用 setState 会导致错误，实际上在整个构建、布局和绘制阶段都不能同步调用 setState，这是因为，在这些阶段调用 setState 都有可能请求新的 frame，都可能会导致循环调用，因此如果要在这些阶段更新应用状态时，都不能直接调用 setState。</p><h4 id="安全更新" tabindex="-1">安全更新 <a class="header-anchor" href="#安全更新" aria-label="Permalink to &quot;安全更新&quot;">​</a></h4><p>现在我们知道在 build 阶段不能调用 setState了，实际上在组件的布局阶段和绘制阶段也都不能直接再同步请求重新布局或重绘，道理是相同的，那在这些阶段正确的更新方式是什么呢，我们以 setState 为例，可以通过如下方式：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#6A737D;">// 在build、布局、绘制阶段安全更新</span></span>
<span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">update</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">VoidCallback</span><span style="color:#E1E4E8;"> fn) {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#79B8FF;">SchedulerBinding</span><span style="color:#E1E4E8;">.instance.</span><span style="color:#B392F0;">addPostFrameCallback</span><span style="color:#E1E4E8;">((_) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#B392F0;">setState</span><span style="color:#E1E4E8;">(fn);</span></span>
<span class="line"><span style="color:#E1E4E8;">  });</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#6A737D;">// 在build、布局、绘制阶段安全更新</span></span>
<span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">update</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">VoidCallback</span><span style="color:#24292E;"> fn) {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#005CC5;">SchedulerBinding</span><span style="color:#24292E;">.instance.</span><span style="color:#6F42C1;">addPostFrameCallback</span><span style="color:#24292E;">((_) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#6F42C1;">setState</span><span style="color:#24292E;">(fn);</span></span>
<span class="line"><span style="color:#24292E;">  });</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>注意，update 函数只应该在 frame 执行 <code>persistentCallbacks</code> 时执行，其他阶段直接调用 setState 即可。因为 idle 状态会是一个特例，如果 在idle 状态调用 update 的话，需要手动调用 <code>scheduleFrame()</code> 请求新的 frame，否则 postFrameCallbacks 在下一个frame （其他组件请求的 frame ）到来之前不会被执行，因此我们可以将 update 修改一下：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#F97583;">void</span><span style="color:#E1E4E8;"> </span><span style="color:#B392F0;">update</span><span style="color:#E1E4E8;">(</span><span style="color:#79B8FF;">VoidCallback</span><span style="color:#E1E4E8;"> fn) {</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">final</span><span style="color:#E1E4E8;"> schedulerPhase </span><span style="color:#F97583;">=</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">SchedulerBinding</span><span style="color:#E1E4E8;">.instance.schedulerPhase;</span></span>
<span class="line"><span style="color:#E1E4E8;">  </span><span style="color:#F97583;">if</span><span style="color:#E1E4E8;"> (schedulerPhase </span><span style="color:#F97583;">==</span><span style="color:#E1E4E8;"> </span><span style="color:#79B8FF;">SchedulerPhase</span><span style="color:#E1E4E8;">.persistentCallbacks) {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#79B8FF;">SchedulerBinding</span><span style="color:#E1E4E8;">.instance.</span><span style="color:#B392F0;">addPostFrameCallback</span><span style="color:#E1E4E8;">((_) {</span></span>
<span class="line"><span style="color:#E1E4E8;">      </span><span style="color:#B392F0;">setState</span><span style="color:#E1E4E8;">(fn);</span></span>
<span class="line"><span style="color:#E1E4E8;">    });</span></span>
<span class="line"><span style="color:#E1E4E8;">  } </span><span style="color:#F97583;">else</span><span style="color:#E1E4E8;"> {</span></span>
<span class="line"><span style="color:#E1E4E8;">    </span><span style="color:#B392F0;">setState</span><span style="color:#E1E4E8;">(fn);</span></span>
<span class="line"><span style="color:#E1E4E8;">  }</span></span>
<span class="line"><span style="color:#E1E4E8;">}</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#D73A49;">void</span><span style="color:#24292E;"> </span><span style="color:#6F42C1;">update</span><span style="color:#24292E;">(</span><span style="color:#005CC5;">VoidCallback</span><span style="color:#24292E;"> fn) {</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">final</span><span style="color:#24292E;"> schedulerPhase </span><span style="color:#D73A49;">=</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">SchedulerBinding</span><span style="color:#24292E;">.instance.schedulerPhase;</span></span>
<span class="line"><span style="color:#24292E;">  </span><span style="color:#D73A49;">if</span><span style="color:#24292E;"> (schedulerPhase </span><span style="color:#D73A49;">==</span><span style="color:#24292E;"> </span><span style="color:#005CC5;">SchedulerPhase</span><span style="color:#24292E;">.persistentCallbacks) {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#005CC5;">SchedulerBinding</span><span style="color:#24292E;">.instance.</span><span style="color:#6F42C1;">addPostFrameCallback</span><span style="color:#24292E;">((_) {</span></span>
<span class="line"><span style="color:#24292E;">      </span><span style="color:#6F42C1;">setState</span><span style="color:#24292E;">(fn);</span></span>
<span class="line"><span style="color:#24292E;">    });</span></span>
<span class="line"><span style="color:#24292E;">  } </span><span style="color:#D73A49;">else</span><span style="color:#24292E;"> {</span></span>
<span class="line"><span style="color:#24292E;">    </span><span style="color:#6F42C1;">setState</span><span style="color:#24292E;">(fn);</span></span>
<span class="line"><span style="color:#24292E;">  }</span></span>
<span class="line"><span style="color:#24292E;">}</span></span></code></pre></div><p>至此，我们封装了一个可以安全更新状态的 update 函数。</p><p>现在我们回想一下，在第十章 “自绘组件：CustomCheckbox” 一节中，为了执行动画，我们在绘制完成之后通过如下代码请求重绘：</p><div class="language-dart vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">dart</span><pre class="shiki github-dark vp-code-dark"><code><span class="line"><span style="color:#79B8FF;">SchedulerBinding</span><span style="color:#E1E4E8;">.instance.</span><span style="color:#B392F0;">addPostFrameCallback</span><span style="color:#E1E4E8;">((_) {</span></span>
<span class="line"><span style="color:#E1E4E8;">   ...</span></span>
<span class="line"><span style="color:#E1E4E8;">   </span><span style="color:#B392F0;">markNeedsPaint</span><span style="color:#E1E4E8;">();</span></span>
<span class="line"><span style="color:#E1E4E8;"> });</span></span></code></pre><pre class="shiki github-light vp-code-light"><code><span class="line"><span style="color:#005CC5;">SchedulerBinding</span><span style="color:#24292E;">.instance.</span><span style="color:#6F42C1;">addPostFrameCallback</span><span style="color:#24292E;">((_) {</span></span>
<span class="line"><span style="color:#24292E;">   ...</span></span>
<span class="line"><span style="color:#24292E;">   </span><span style="color:#6F42C1;">markNeedsPaint</span><span style="color:#24292E;">();</span></span>
<span class="line"><span style="color:#24292E;"> });</span></span></code></pre></div><p>我们并没有直接调用 markNeedsPaint()，而原因正如上面所述。</p><h2 id="_14-3-3-总结" tabindex="-1">14.3.3 总结 <a class="header-anchor" href="#_14-3-3-总结" aria-label="Permalink to &quot;14.3.3 总结&quot;">​</a></h2><p>本节介绍了Flutter App 从启动到显示到屏幕上的主流程，重点是 Flutter 的渲染流程，如图14-4：<img src="/assets/14-4.fa137c96.png" alt="图14-4"></p><p>需要说明的是 build 过程和 layout 过程是可以交替执行的，这个我们在介绍 LayoutBuilder 一节时已经解释过了。读者需要对整个渲染流程有个大概印象，后面我们会详细介绍，不过在深入介绍渲染管线之前，我们得仔细的了解一下 Element 、BuildContext 和 RenderObject 三个类。</p></div></div></main><footer class="VPDocFooter" data-v-6b87e69f data-v-ef5dee53><!--[--><!--]--><div class="edit-info" data-v-ef5dee53><div class="edit-link" data-v-ef5dee53><a class="VPLink link vp-external-link-icon no-icon edit-link-button" href="https://gitee.com/luoguangguang/note/tree/master/docs/largeFrontEnd/flutter/chapter14/flutter_app_startup.md" target="_blank" rel="noreferrer" data-v-ef5dee53><!--[--><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" class="edit-link-icon" aria-label="edit icon" data-v-ef5dee53><path d="M18,23H4c-1.7,0-3-1.3-3-3V6c0-1.7,1.3-3,3-3h7c0.6,0,1,0.4,1,1s-0.4,1-1,1H4C3.4,5,3,5.4,3,6v14c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1v-7c0-0.6,0.4-1,1-1s1,0.4,1,1v7C21,21.7,19.7,23,18,23z"></path><path d="M8,17c-0.3,0-0.5-0.1-0.7-0.3C7,16.5,6.9,16.1,7,15.8l1-4c0-0.2,0.1-0.3,0.3-0.5l9.5-9.5c1.2-1.2,3.2-1.2,4.4,0c1.2,1.2,1.2,3.2,0,4.4l-9.5,9.5c-0.1,0.1-0.3,0.2-0.5,0.3l-4,1C8.2,17,8.1,17,8,17zM9.9,12.5l-0.5,2.1l2.1-0.5l9.3-9.3c0.4-0.4,0.4-1.1,0-1.6c-0.4-0.4-1.2-0.4-1.6,0l0,0L9.9,12.5z M18.5,2.5L18.5,2.5L18.5,2.5z"></path></svg> 在Gitee上参与编辑此页<!--]--></a></div><!----></div><nav class="prev-next" data-v-ef5dee53><div class="pager" data-v-ef5dee53><!----></div><div class="pager" data-v-ef5dee53><a class="pager-link next" href="./largeFrontEnd/flutter/chapter1/index.html" data-v-ef5dee53><span class="desc" data-v-ef5dee53>Next page</span><span class="title" data-v-ef5dee53>第一章 初识Flutter</span></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><footer class="VPFooter has-sidebar" data-v-1919c326 data-v-e03eb2e1><div class="container" data-v-e03eb2e1><p class="message" data-v-e03eb2e1>Released under the MIT License.</p><p class="copyright" data-v-e03eb2e1>Copyright © 2023-present LG of GZU</p></div></footer><!--[--><!--]--></div></div>
    <script>window.__VP_HASH_MAP__=JSON.parse("{\"backend_index.md\":\"0cb07c7c\",\"largefrontend_electron_index.md\":\"adab8e13\",\"largefrontend_flutter_chapter1_index.md\":\"5106cc23\",\"index.md\":\"20a66851\",\"largefrontend_flutter_chapter10_intro.md\":\"5756d1e8\",\"largefrontend_flutter_chapter10_index.md\":\"87e47f16\",\"largefrontend_flutter_chapter1_flutter_intro.md\":\"9ad5fc78\",\"largefrontend_flutter_chapter12_package_and_plugin.md\":\"c2f14cd7\",\"largefrontend_flutter_chapter12_flutter_web.md\":\"f4b6b743\",\"largefrontend_flutter_chapter10_combine.md\":\"333319d8\",\"largefrontend_flutter_chapter14_flutter_ui_system.md\":\"a1011f7b\",\"largefrontend_flutter_chapter11_index.md\":\"35dcdd7c\",\"largefrontend_flutter_chapter12_index.md\":\"890b799c\",\"largefrontend_flutter_chapter13_index.md\":\"6db9b579\",\"largefrontend_flutter_chapter14_index.md\":\"87f7da6b\",\"largefrontend_flutter_chapter1_mobile_development_intro.md\":\"40c15ac2\",\"largefrontend_flutter_chapter10_turn_box.md\":\"6a792fe1\",\"largefrontend_flutter_chapter3_index.md\":\"bc1d0de5\",\"largefrontend_flutter_chapter4_stack.md\":\"3f5c3186\",\"largefrontend_flutter_chapter1_install_flutter.md\":\"4543c368\",\"largefrontend_flutter_chapter14_paint.md\":\"8f7289b0\",\"largefrontend_flutter_chapter5_padding.md\":\"9fadce05\",\"largefrontend_flutter_chapter4_index.md\":\"f9b298f7\",\"largefrontend_flutter_chapter2_flutter_package_mgr.md\":\"4a3b7161\",\"largefrontend_flutter_chapter11_socket.md\":\"e8264e44\",\"largefrontend_flutter_chapter4_intro.md\":\"1341b5fd\",\"largefrontend_flutter_chapter14_update.md\":\"e4d2c2f0\",\"largefrontend_flutter_chapter5_index.md\":\"455baa0a\",\"largefrontend_flutter_chapter4_alignment.md\":\"a6596610\",\"largefrontend_flutter_chapter13_multi_languages_support.md\":\"c113238d\",\"largefrontend_flutter_chapter11_dio.md\":\"64f4e813\",\"largefrontend_flutter_chapter2_first_flutter_app.md\":\"b650181d\",\"largefrontend_flutter_chapter2_flutter_app_debug.md\":\"67a72433\",\"largefrontend_flutter_chapter2_state_manage.md\":\"9bb80d37\",\"largefrontend_flutter_chapter6_index.md\":\"5a321b81\",\"largefrontend_flutter_chapter5_material_scaffold.md\":\"8e6b896a\",\"largefrontend_flutter_chapter3_buttons.md\":\"ef950360\",\"largefrontend_flutter_chapter10_done_widget.md\":\"c3b44318\",\"largefrontend_flutter_chapter6_single_child_scrollview.md\":\"e3f75f3b\",\"largefrontend_flutter_chapter14_paint_flow.md\":\"d2f8102a\",\"largefrontend_flutter_chapter6_intro.md\":\"125a064e\",\"largefrontend_flutter_chapter5_transform.md\":\"22708e1e\",\"largefrontend_flutter_chapter14_layer.md\":\"4ed449fc\",\"largefrontend_flutter_chapter11_websocket.md\":\"0a022e47\",\"largefrontend_flutter_chapter5_fittedbox.md\":\"db915726\",\"largefrontend_flutter_chapter2_index.md\":\"d8e937b7\",\"largefrontend_flutter_chapter3_radio_and_checkbox.md\":\"025aba21\",\"largefrontend_flutter_chapter13_locallization_implement.md\":\"dc4b83fe\",\"largefrontend_flutter_chapter2_thread_model_and_error_report.md\":\"a29199d6\",\"largefrontend_flutter_chapter5_decoratedbox.md\":\"20f093ba\",\"largefrontend_flutter_chapter14_render_object.md\":\"55a1b551\",\"largefrontend_flutter_chapter14_element_buildcontext.md\":\"4272eae4\",\"largefrontend_flutter_chapter4_wrap_and_flow.md\":\"ac41b5bd\",\"largefrontend_flutter_chapter11_file_operation.md\":\"996a3a5d\",\"largefrontend_flutter_chapter13_faq.md\":\"ba7b9c70\",\"largefrontend_webfrontend_md_10.md\":\"51e0a312\",\"largefrontend_webfrontend_md_24.md\":\"0a4abd08\",\"largefrontend_flutter_chapter14_flutter_app_startup.md\":\"9b5942a1\",\"largefrontend_flutter_chapter10_custom_paint.md\":\"8b2a0a46\",\"largefrontend_flutter_chapter2_flutter_assets_mgr.md\":\"0e38b701\",\"largefrontend_interview_01.md\":\"2ac4b56d\",\"largefrontend_flutter_chapter8_index.md\":\"e83d3d80\",\"largefrontend_webfrontend_md_16.md\":\"a5610416\",\"largefrontend_flutter_chapter9_index.md\":\"3fb342a7\",\"largefrontend_flutter_chapter8_listener.md\":\"1b9b0316\",\"largefrontend_webfrontend_md_37.md\":\"3cacf3a2\",\"largefrontend_flutter_chapter4_layoutbuilder.md\":\"3d4bf94b\",\"largefrontend_webfrontend_md_17.md\":\"ee74edfb\",\"largefrontend_webfrontend_md_13.md\":\"b180bb5b\",\"largefrontend_flutter_chapter7_index.md\":\"0eb456b2\",\"largefrontend_webfrontend_md_31.md\":\"309453c7\",\"largefrontend_webfrontend_md_25.md\":\"95fdb4fe\",\"largefrontend_webfrontend_md_12.md\":\"3b14e491\",\"largefrontend_webfrontend_md_23.md\":\"18f08d72\",\"largefrontend_flutter_chapter11_json_model.md\":\"c84d2ccf\",\"largefrontend_webfrontend_md_01.md\":\"fbb7a35f\",\"largefrontend_flutter_chapter3_progress.md\":\"9bc148d3\",\"largefrontend_webfrontend_md_18.md\":\"ee651cc6\",\"largefrontend_flutter_sponsor.md\":\"e71431a9\",\"largefrontend_webfrontend_md_36.md\":\"f22f6d0d\",\"largefrontend_webfrontend_md_07.md\":\"71c2ad1d\",\"largefrontend_webfrontend_md_19.md\":\"e7907728\",\"largefrontend_webfrontend_md_20.md\":\"5a68bb4f\",\"largefrontend_webfrontend_md_32.md\":\"91d2f39f\",\"largefrontend_flutter_chapter7_futurebuilder_and_streambuilder.md\":\"af3535c0\",\"largefrontend_flutter_chapter9_hero.md\":\"25b46cf2\",\"largefrontend_flutter_chapter8_notification.md\":\"909f6756\",\"largefrontend_webfrontend_md_08.md\":\"0d59dce4\",\"largefrontend_flutter_chapter3_img_and_icon.md\":\"8b409b55\",\"largefrontend_webfrontend_md_04.md\":\"d6c15d55\",\"largefrontend_flutter_chapter9_animated_switcher.md\":\"9c0b3119\",\"largefrontend_webfrontend_md_22.md\":\"7d6fea68\",\"largefrontend_flutter_chapter2_flutter_widget_intro.md\":\"13414fea\",\"largefrontend_flutter_chapter13_intl.md\":\"d4776610\",\"largefrontend_overview_index.md\":\"3c87f69c\",\"largefrontend_flutter_chapter5_container.md\":\"373cc694\",\"largefrontend_wechatminiprogram_index.md\":\"7f40708d\",\"largefrontend_webfrontend_md_38.md\":\"3b6bb332\",\"linux_index.md\":\"624ecc66\",\"largefrontend_webfrontend_md_35.md\":\"f0538423\",\"myteam_index.md\":\"91a2c11f\",\"largefrontend_flutter_chapter6_pageview.md\":\"36fcbc65\",\"largefrontend_webfrontend_md_03.md\":\"2e5087f0\",\"largefrontend_webfrontend_md_21.md\":\"c181316e\",\"largefrontend_flutter_chapter3_input_and_form.md\":\"e4fba9dd\",\"largefrontend_flutter_chapter14_compositing.md\":\"6d96f932\",\"largefrontend_webfrontend_md_29.md\":\"c6fada7d\",\"largefrontend_flutter_chapter4_constraints.md\":\"9e4baace\",\"largefrontend_webfrontend_md_11.md\":\"63cd9c49\",\"largefrontend_webfrontend_md_14.md\":\"1423aeb1\",\"largefrontend_flutter_chapter6_tabview.md\":\"56048fb1\",\"largefrontend_webfrontend_md_33.md\":\"fdbe86fa\",\"largefrontend_flutter_chapter6_nestedscrollview.md\":\"2a66b86a\",\"largefrontend_webfrontend_md_34.md\":\"ad8ea220\",\"largefrontend_flutter_chapter2_flutter_router.md\":\"bb6e776c\",\"largefrontend_webfrontend_md_30.md\":\"cd2cb5aa\",\"largefrontend_flutter_chapter4_flex.md\":\"ebd43d6a\",\"largefrontend_webfrontend_md_09.md\":\"eedafcb1\",\"largefrontend_webfrontend_md_40.md\":\"4d4fefa1\",\"largefrontend_flutter_join_us.md\":\"44c81d86\",\"largefrontend_flutter_chapter9_route_transition.md\":\"f56cf837\",\"largefrontend_uniapp_index.md\":\"fa387c39\",\"largefrontend_flutter_chapter4_row_and_column.md\":\"964229f5\",\"largefrontend_flutter_chapter9_stagger_animation.md\":\"315538f5\",\"largefrontend_flutter_chapter3_text.md\":\"6720230c\",\"largefrontend_flutter_chapter10_watermark.md\":\"2e4ddf03\",\"largefrontend_flutter_chapter11_http.md\":\"efd2ee64\",\"largefrontend_webfrontend_md_06.md\":\"e20b049a\",\"largefrontend_flutter_summary.md\":\"2e31b0a8\",\"largefrontend_flutter_chapter6_animatedlist.md\":\"20d9436f\",\"largefrontend_flutter_chapter5_clip.md\":\"91f4174c\",\"largefrontend_webfrontend_md_02.md\":\"40cd2099\",\"largefrontend_flutter_chapter6_gridview.md\":\"b3be7e60\",\"largefrontend_flutter_chapter6_custom_scrollview.md\":\"01be950c\",\"largefrontend_flutter_chapter11_download_with_chunks.md\":\"9379cc89\",\"largefrontend_flutter_chapter6_scroll_controller.md\":\"ffa2d5fd\",\"largefrontend_flutter_chapter9_intro.md\":\"3fc29b5f\",\"largefrontend_flutter_chapter1_dart.md\":\"6bba4e7c\",\"largefrontend_flutter_chapter7_inherited_widget.md\":\"5a21d704\",\"largefrontend_flutter_index.md\":\"cd81fbbe\",\"largefrontend_flutter_reference.md\":\"36336375\",\"largefrontend_flutter_chapter6_keepalive.md\":\"535137be\",\"largefrontend_flutter_chapter14_layout.md\":\"0d7a8df3\",\"largefrontend_flutter_chapter6_sliver.md\":\"0c8b7553\",\"largefrontend_webfrontend_md_39.md\":\"aef49a57\",\"largefrontend_webfrontend_md_15.md\":\"bf9ff1a1\",\"largefrontend_flutter_chapter14_image_and_cache.md\":\"9622c56b\",\"largefrontend_webfrontend_md_28.md\":\"83169d10\",\"largefrontend_flutter_chapter8_eventbus.md\":\"fc0db86d\",\"largefrontend_flutter_chapter7_value_listenable_builder.md\":\"a0c76121\",\"largefrontend_flutter_preface.md\":\"604b9934\",\"largefrontend_flutter_chapter7_theme.md\":\"c21036bc\",\"largefrontend_webfrontend_md_05.md\":\"f7607009\",\"largefrontend_flutter_chapter6_listview.md\":\"472910bd\",\"largefrontend_flutter_chapter7_provider.md\":\"ab590744\",\"largefrontend_flutter_chapter8_gesture.md\":\"859bbb38\",\"largefrontend_flutter_chapter9_animated_widgets.md\":\"e8cd3c4d\",\"largefrontend_webfrontend_md_26.md\":\"4928006b\",\"largefrontend_flutter_chapter10_gradient_circular_progress_demo.md\":\"b3d48a2c\",\"largefrontend_flutter_chapter8_hittest.md\":\"e963812b\",\"largefrontend_flutter_chapter7_willpopscope.md\":\"d224ecce\",\"largefrontend_flutter_chapter9_animation_structure.md\":\"672eb3c0\",\"largefrontend_flutter_chapter8_gesture_conflict.md\":\"15bbadba\",\"largefrontend_webfrontend_md_27.md\":\"0b5b5e39\",\"largefrontend_flutter_chapter7_dailog.md\":\"5f91e51a\",\"largefrontend_flutter_chapter10_custom_checkbox.md\":\"0db7d50f\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"zh-CN\",\"dir\":\"ltr\",\"title\":\"落光的Pro博客\",\"description\":\"Vite & Vue powered static site generator.\",\"base\":\"./\",\"head\":[],\"appearance\":true,\"themeConfig\":{\"search\":{\"provider\":\"local\",\"options\":{\"locales\":{\"zh\":{\"translations\":{\"button\":{\"buttonText\":\"搜索文档\",\"buttonAriaLabel\":\"搜索文档\"},\"modal\":{\"noResultsText\":\"无法找到相关结果\",\"resetButtonTitle\":\"清除查询条件\",\"footer\":{\"selectText\":\"选择\",\"navigateText\":\"切换\"}}}}}}},\"logo\":\"/icon-radius.png\",\"nav\":[{\"text\":\"大前端\",\"items\":[{\"text\":\"Web前端\",\"link\":\"/largeFrontEnd/webFrontEnd/md/01.md\"},{\"text\":\"Uni-app\",\"link\":\"/largeFrontEnd/uniapp/\"},{\"text\":\"微信小程序\",\"link\":\"/largeFrontEnd/weChatMiniProgram/\"},{\"text\":\"Electron\",\"link\":\"/largeFrontEnd/electron/\"},{\"text\":\"Flutter\",\"link\":\"/largeFrontEnd/flutter/\"},{\"text\":\"前端面试题\",\"link\":\"/largeFrontEnd/interview/01.md\"}]},{\"text\":\"后端\",\"link\":\"/backEnd/\"},{\"text\":\"Linux\",\"link\":\"/linux/\"},{\"text\":\"我的团队\",\"link\":\"/myTeam/\"}],\"sidebar\":{\"/largeFrontEnd/webFrontEnd/\":[{\"text\":\"第一阶段  基础入门\",\"collapsed\":true,\"items\":[{\"text\":\"一、基础认知\",\"link\":\"/largeFrontEnd/webFrontEnd/md/01.md\"},{\"text\":\"二、HTML标签学习\",\"link\":\"/largeFrontEnd/webFrontEnd/md/02.md\"},{\"text\":\"三、HTML基础\",\"link\":\"/largeFrontEnd/webFrontEnd/md/03.md\"},{\"text\":\"四、CSS基础\",\"link\":\"/largeFrontEnd/webFrontEnd/md/04.md\"},{\"text\":\"五、CSS进阶\",\"link\":\"/largeFrontEnd/webFrontEnd/md/05.md\"},{\"text\":\"六、盒子模型\",\"link\":\"/largeFrontEnd/webFrontEnd/md/06.md\"},{\"text\":\"七、CSS浮动\",\"link\":\"/largeFrontEnd/webFrontEnd/md/07.md\"},{\"text\":\"八、CSS定位装饰\",\"link\":\"/largeFrontEnd/webFrontEnd/md/08.md\"},{\"text\":\"九、CSS精灵图\",\"link\":\"/largeFrontEnd/webFrontEnd/md/09.md\"},{\"text\":\"项目前置认知\",\"link\":\"/largeFrontEnd/webFrontEnd/md/10.md\"},{\"text\":\"项目结构搭建\",\"link\":\"/largeFrontEnd/webFrontEnd/md/11.md\"},{\"text\":\"字体图标、动画\",\"link\":\"/largeFrontEnd/webFrontEnd/md/12.md\"},{\"text\":\"移动web开发\",\"link\":\"/largeFrontEnd/webFrontEnd/md/13.md\"}]},{\"text\":\"第二阶段 技术进阶\",\"collapsed\":true,\"items\":[{\"text\":\"JavaScript深入浅出\",\"link\":\"/largeFrontEnd/webFrontEnd/md/14.md\"},{\"text\":\"JavaScript核心之web APIs\",\"link\":\"/largeFrontEnd/webFrontEnd/md/15.md\"},{\"text\":\"一、Web APIS导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/16.md\"},{\"text\":\"二、DOM导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/17.md\"},{\"text\":\"三、事件高级导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/18.md\"},{\"text\":\"四、BOM导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/19.md\"},{\"text\":\"五、PC网页特效导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/20.md\"},{\"text\":\"六、移动端特效导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/21.md\"},{\"text\":\"七、本地存储导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/22.md\"},{\"text\":\"jQuery入门导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/23.md\"},{\"text\":\"数据可视化项目导读\",\"link\":\"/largeFrontEnd/webFrontEnd/md/24.md\"}]},{\"text\":\"Node.js\",\"collapsed\":true,\"items\":[{\"text\":\"buffer(缓冲器)\",\"link\":\"/largeFrontEnd/webFrontEnd/md/25.md\"},{\"text\":\"计算机基础\",\"link\":\"/largeFrontEnd/webFrontEnd/md/26.md\"},{\"text\":\"fs模块\",\"link\":\"/largeFrontEnd/webFrontEnd/md/27.md\"},{\"text\":\"path模块\",\"link\":\"/largeFrontEnd/webFrontEnd/md/28.md\"},{\"text\":\"HTTP模块\",\"link\":\"/largeFrontEnd/webFrontEnd/md/29.md\"},{\"text\":\"Express框架\",\"link\":\"/largeFrontEnd/webFrontEnd/md/30.md\"},{\"text\":\"Mongodb\",\"link\":\"/largeFrontEnd/webFrontEnd/md/31.md\"}]},{\"text\":\"Ajax\",\"link\":\"/largeFrontEnd/webFrontEnd/md/32.md\"},{\"text\":\"前端面试题\",\"collapsed\":true,\"items\":[{\"text\":\"一、CSS\",\"link\":\"/largeFrontEnd/webFrontEnd/md/33.md\"},{\"text\":\"二、JavaScript\",\"link\":\"/largeFrontEnd/webFrontEnd/md/34.md\"},{\"text\":\"三、HTML5CSS3\",\"link\":\"/largeFrontEnd/webFrontEnd/md/35.md\"},{\"text\":\"四、Vue\",\"link\":\"/largeFrontEnd/webFrontEnd/md/36.md\"},{\"text\":\"五、Echarts\",\"link\":\"/largeFrontEnd/webFrontEnd/md/37.md\"},{\"text\":\"六.Uni-APP\",\"link\":\"/largeFrontEnd/webFrontEnd/md/38.md\"},{\"text\":\"七、webpack\",\"link\":\"/largeFrontEnd/webFrontEnd/md/39.md\"},{\"text\":\"八、Git\",\"link\":\"/largeFrontEnd/webFrontEnd/md/40.md\"}]}],\"/largeFrontEnd/flutter/\":[{\"text\":\"Flutter入门到实战\",\"collapsed\":false,\"items\":[{\"text\":\"第一章 初识Flutter\",\"link\":\"/largeFrontEnd/flutter/chapter1/index.md\"},{\"text\":\"第二章 简介\",\"link\":\"/largeFrontEnd/flutter/chapter2/index.md\"},{\"text\":\"第三章 基础组件\",\"link\":\"/largeFrontEnd/flutter/chapter3/index.md\"},{\"text\":\"第四章 布局类组件\",\"link\":\"/largeFrontEnd/flutter/chapter4/index.md\"},{\"text\":\"第五章 容器类Widget\",\"link\":\"/largeFrontEnd/flutter/chapter5/index.md\"},{\"text\":\"第六章 可滚动组件\",\"link\":\"/largeFrontEnd/flutter/chapter6/index.md\"},{\"text\":\"第七章 功能型Widget简介\",\"link\":\"/largeFrontEnd/flutter/chapter7/index.md\"},{\"text\":\"第八章 事件处理与通知\",\"link\":\"/largeFrontEnd/flutter/chapter8/index.md\"},{\"text\":\"第九章 动画\",\"link\":\"/largeFrontEnd/flutter/chapter9/index.md\"},{\"text\":\"第十章 自定义组件\",\"link\":\"/largeFrontEnd/flutter/chapter10/index.md\"},{\"text\":\"第十一章 文件操作\",\"link\":\"/largeFrontEnd/flutter/chapter11/index.md\"},{\"text\":\"第十二章 Flutter 扩展\",\"link\":\"/largeFrontEnd/flutter/chapter12/index.md\"},{\"text\":\"第十三章 多语言\",\"link\":\"/largeFrontEnd/flutter/chapter13/index.md\"},{\"text\":\"第十四章 高级进阶\",\"link\":\"/largeFrontEnd/flutter/chapter14/index.md\"}]}]},\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://gitee.com/luoguangguang\"},{\"icon\":{\"svg\":\"<svg role=\\\"img\\\" viewBox=\\\"0 0 24 24\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\"><title>Dribbble</title><path d=\\\"M12...6.38z\\\"/></svg>\"},\"link\":\"...\",\"ariaLabel\":\"cool link\"}],\"footer\":{\"message\":\"Released under the MIT License.\",\"copyright\":\"Copyright © 2023-present LG of GZU\"},\"editLink\":{\"pattern\":\"https://gitee.com/luoguangguang/note/tree/master/docs/:path\",\"text\":\"在Gitee上参与编辑此页\"},\"carbonAds\":{\"code\":\"your-carbon-code\",\"placement\":\"your-carbon-placement\"}},\"locales\":{},\"scrollOffset\":90,\"cleanUrls\":false}");</script>
    
  </body>
</html>